#pragma once

#include <boost/shared_ptr.hpp>

#include "wrapper.h"
#include "memory_map_file.h"
#include "disk_controller_constants.h"

using namespace boost;

#define CONFIG_FILE_NAME            ("config.ini")
#define CONFIG_BASE_ADDRESS         ("Config.BaseAddress")
#define CONFIG_SECTORS_PER_TRACK    ("Config.SectorPerTrack")
#define CONFIG_HEAD_COUNT           ("Config.HeadCount")
#define CONFIG_CYLINDER_HI_COUNT    ("Config.CylinderHiCount")
#define CONFIG_SECTOR_SIZE          ("Config.SectorSize")
#define CONFIG_HARD_DISK_ADDRESS    ("Config.HardDiskAddress")
#define CONFIG_DMA_ADDRESS          ("Config.DmaAddress")

class Wrapper;
/* HardDiskController class
 * handles the IN/OUT opcodes and simulates UM83C001 Hard Disk Controller
 */
class HardDiskController {
    private:
        /* pointer to parent Wrapper instance, used to read/write debuggee memory */
        Wrapper* wrap;
        /* base port number (to A0) */
        uint baseAddress;
        /* size of sector (in bytes) */
        uint sectorSize;
        /* cylinderHiCount * 0x100 = number of cylinders */
        uchar cylinderHiCount;
        /* number of heads in disk */
        uchar headCount;        
        /* total disk size (in bytes) */
        uint diskSize;
        /* the hard disk data will be mapped to this address with nmap */
        uint hardDiskAddress;
        /* the fake dma address in the debuggee (contains one sector)*/
        uint dmaAddress;
        shared_ptr<MemoryMappedFile> hardDiskfile;
        // Address mode (see table 2. Register Addressing)
        uchar registerAddress;
        bool autoIncrement;
        
        // Registers        
        uchar sectorsPerTrack;                
        uchar status;
        uchar sector;
        uchar head;
        uchar transferCount;
        uchar diskOp;
        uchar cylinderHi;
        uchar cylinderLow;
        void init(uint baseAddress, uchar sectorsPerTrack,
                  uchar headCount, uchar cyinderHiCount,
                  uint sectorSize, uint hardDiskAddress,
                  uint dmaAddress);   
        /* helper: returns pointer to the head of the current sector */
        void* getAddress();
        void* getAddress(uchar sector, uchar head, uchar cylinderHi, uchar cylinderLow);
        /* helper: if autoIncrement then registerAddress++ */
        void handleAutoInc();
    public:
        /* c'tor, reads params from "config.ini" file */
        HardDiskController(Wrapper* wrap);
        ~HardDiskController();
        void outp(uint port, uchar value);
        uchar inp(uint port);
};